package demo.transform;

import org.lwjgl.opengl.GL11;

import engine.camera.FlyoverCamera;
import engine.core.scene.Scene;
import engine.gameObject.GameObject;
import engine.gui.GUIStyle;
import engine.light.PointLight;
import engine.material.Material;
import engine.sound.Sound;
import engine.util.draw.Disk;
import engine.util.draw.Sphere;
import engine.util.gui.Profiler;
import engine.util.math.Quaternion;
import engine.util.texture.TextureLoader;

public class TransformScene extends Scene {

	private static int PLANETCOUNT = 9;
	
	private static float SUNSIZE = 10.0f;
	
	private static float MOON_MIN_SIZE = 0.15f;
	private static float MOON_MAX_SIZE = 0.9f;
	
	private static float PLANET_MIN_SIZE = 1.0f;
	private static float PLANET_MAX_SIZE = 4.0f;

	private static float MOON_PLANET_DISTANCE = MOON_MAX_SIZE + PLANET_MAX_SIZE + 1.0f;
	
	private static float PLANETS_MIN_DISTANCE = 2 * (MOON_PLANET_DISTANCE + MOON_MAX_SIZE) + 0.0f;
	private static float PLANETS_MAX_DISTANCE = PLANETS_MIN_DISTANCE + 0.0f;
	
	private static float PLANET_SUN_DISTANCE = SUNSIZE + MOON_PLANET_DISTANCE + MOON_MAX_SIZE + 0.0f;
	
	
	private float[] sunSystemStartPosition = new float[]{0, 0, 0};
	
	private Sphere sun;

	private Disk[] orbitPlanets = new Disk[PLANETCOUNT];
	
	private Sphere[] planets = new Sphere[PLANETCOUNT];
	
	private Disk[] orbitMoon1Planets = new Disk[PLANETCOUNT];
	private Disk[] orbitMoon2Planets = new Disk[PLANETCOUNT];
	
	private Sphere[] moon1Planets = new Sphere[PLANETCOUNT];
	private Sphere[] moon2Planets = new Sphere[PLANETCOUNT];
	
	private Quaternion rotSun = Quaternion.convRotAxisToQuat((float)Math.toRadians(0.05), 0, 1, 0);
	
	private Quaternion[] rotPlanetOrbits = new Quaternion[PLANETCOUNT];
	
	private Quaternion[] rotPlanets = new Quaternion[PLANETCOUNT];
	
	private Quaternion[] rotMoonOrbits = new Quaternion[2 * PLANETCOUNT];
	
	public TransformScene() {
		super("transform");
	}

	@Override
	public void init() {
		initGUI();
		
		Sound backgroundMusic = new Sound("sounds/demo/music/Spherical Age Theme.ogg");
		backgroundMusic.play();
		backgroundMusic.setLoop(true);
		
		initCamera();

		initSky();

		initLight();
		
		initSunSystem();
		
		initRotations();
	}

	private void initGUI() {
		setGUIStyle(GUIStyle.getDefaultStyle());
		
		Profiler profiler = new Profiler(0, 0);
		profiler.setAlign(Profiler.ALIGN_LEFT);
		getWindowManager().addComponent(profiler);
	}

	private void initCamera() {
		setCurrentCamera(new FlyoverCamera());
		getCurrentCamera().setNearPane(500);
		getCurrentCamera().setNear(1);
		getCurrentCamera().setFarPane(500);
		((FlyoverCamera)getCurrentCamera()).setPosition(sunSystemStartPosition[0], 
				sunSystemStartPosition[1] + 100, sunSystemStartPosition[2], 0, -Math.PI / 2.0f);
	}

	private void initSky() {
		// a sky object
		Sphere sky = new Sphere(2, 30, 30);
		sky.setTexture(TextureLoader.loadTexture("img/demo/sky/skysphere3.png"));
		setSkyObject(sky);
	}

	private void initLight() {
		PointLight light = new PointLight();
		light.setPosition(sunSystemStartPosition);
		light.setDiffuse(1f, 1f, 1f);
		light.setLinearAttenuation(0.01f);
		addLight(light);
	}
	
	private void initSunSystem() {
		sun = new Sphere(SUNSIZE, 20, 20);
		Material mat = new Material(
				new float[]{0, 0, 0, 0}, 
				new float[]{0, 0, 0, 0}, 
				new float[]{0, 0, 0, 0}, 
				new float[]{1, 0.4f, 0, 0}, 1);
		sun.setMaterial(mat);
		sun.setTexture(TextureLoader.loadTexture("img/demo/planets/planet1.png", GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR, true));
		
		for(int i = 0; i < PLANETCOUNT; i++) {
			float planetSize = PLANET_MIN_SIZE + (float)Math.random() * (PLANET_MAX_SIZE - PLANET_MIN_SIZE);

			moon1Planets[i] = initMoon(MOON_MIN_SIZE + (float)Math.random() * (MOON_MAX_SIZE - MOON_MIN_SIZE), Material.WHITE, MOON_PLANET_DISTANCE);
			moon2Planets[i] = initMoon(MOON_MIN_SIZE + (float)Math.random() * (MOON_MAX_SIZE - MOON_MIN_SIZE), Material.WHITE, -MOON_PLANET_DISTANCE);

			orbitMoon1Planets[i] = new Disk(MOON_PLANET_DISTANCE - 0.025f, MOON_PLANET_DISTANCE + 0.025f, 200, 1);
			orbitMoon1Planets[i].addChild(moon1Planets[i]);	
			orbitMoon1Planets[i].getMaterial().setEmission(0.6f, 0.6f, 0.6f, 1);
			
			orbitMoon2Planets[i] = new Disk(-MOON_PLANET_DISTANCE - 0.025f, -MOON_PLANET_DISTANCE + 0.025f, 200, 1);
			orbitMoon2Planets[i].addChild(moon2Planets[i]);	
			orbitMoon2Planets[i].getMaterial().setEmission(0.6f, 0.6f, 0.6f, 1);
			
			Material planetMaterial = new Material(
					new float[]{0, 0, 0, 0}, 
					new float[]{(float)Math.random(), (float)Math.random(), (float)Math.random(), (float)Math.random()}, 
					new float[]{0, 0, 0, 0}, 
					new float[]{0, 0, 0, 0}, 0);
			
			float distance = PLANET_SUN_DISTANCE + i * PLANETS_MAX_DISTANCE 
			+ (PLANETS_MIN_DISTANCE + (float)Math.random() * (PLANETS_MAX_DISTANCE - PLANETS_MIN_DISTANCE));
			
			planets[i] = initPlanet(planetSize, planetMaterial,
					distance, 
					orbitMoon1Planets[i], orbitMoon2Planets[i]);

			orbitPlanets[i] = new Disk(distance - 0.05f, distance + 0.05f, 200, 1);
			orbitPlanets[i].addChild(planets[i]);
			orbitPlanets[i].getMaterial().setEmission(0.6f, 0.6f, 0.6f, 1);
			orbitPlanets[i].setRotation(Quaternion.convRotAxisToQuat((float)Math.toRadians(Math.random() * 360), 0, 1, 0));
			
			sun.addChild(orbitPlanets[i]);
		}
		
		sun.setPosition(sunSystemStartPosition);
		addGameObject(sun);
	}

	private Sphere initMoon(float size, Material color, float posX) {
		Sphere moon = new Sphere(size, 20, 20);
		moon.setMaterial(color);
		moon.setTexture(TextureLoader.loadTexture("img/demo/planets/planet1.png", GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR, true));
		moon.translate(posX, 0, 0);
		
		return moon;
	}

	private Sphere initPlanet(float size, Material color, float f, GameObject orbitMoon1, GameObject orbitMoon2) {
		Sphere planet = new Sphere(size, 20, 20);
		planet.setMaterial(color);
		planet.setTexture(TextureLoader.loadTexture("img/demo/planets/planet2.png", GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR, true));
		
		planet.addChild(orbitMoon1);
		planet.addChild(orbitMoon2);
		
		planet.translate(f, 0, 0);
		
		return planet;
	}
	
	private void initRotations() {
		for(int i = 0; i < PLANETCOUNT; i++) {
			rotPlanetOrbits[i] = Quaternion.convRotAxisToQuat((float)Math.toRadians(Math.random() * 0.1), 0, 1, 0);
			rotPlanets[i] = Quaternion.convRotAxisToQuat((float)Math.toRadians(Math.random()), 0, 1, 0);
			rotMoonOrbits[i] = Quaternion.convRotAxisToQuat((float)Math.toRadians(Math.random()), (float)Math.random(), 
					(float)Math.random(), (float)Math.random());
			rotMoonOrbits[PLANETCOUNT + i] = Quaternion.convRotAxisToQuat((float)Math.toRadians(Math.random()), (float)Math.random(), 
					(float)Math.random(), (float)Math.random());
		}
	}
	
	@Override
	public void render(float timeElapsed) {
		sun.rotate(rotSun);

		for(int i = 0; i < PLANETCOUNT; i++) {
			orbitPlanets[i].rotate(rotPlanetOrbits[i]);
			planets[i].rotate(rotPlanets[i]);
			orbitMoon1Planets[i].rotate(rotMoonOrbits[2 * i]);
			orbitMoon2Planets[i].rotate(rotMoonOrbits[2 * i + 1]);
		}
	
		super.render(timeElapsed);
	}	
}
